import java.io.Serializable;
import java.nio.ByteBuffer;


public class RtpPacket implements Serializable {
	
	/*
	 * RTP Header hat 12 Bytes, die anderen optionalen Felder(2*32 Bit, CSRC, Header extension) werden nicht implementiert  
	 */
	
	//Serializable
	private static final long serialVersionUID = 1L;
	
	//Defines
	public static final int NUMBER_OF_MAX_VALUE_1_BIT = 1;
	public static final int NUMBER_OF_MAX_VALUE_2_BIT = 4;
	public static final int NUMBER_OF_MAX_VALUE_4_BIT = 16;
	public static final int NUMBER_OF_MAX_VALUE_7_BIT = 128;
	public static final int NUMBER_OF_MAX_VALUE_16_BIT = 65536;
	public static final long NUMBER_OF_MAX_VALUE_32_BIT = 4294967296L;
	
	//Header
	private int V = 0;		//Version(2 Bit)
	private int P = 0;  	//Padding(1 Bit)
	private int X = 0;		//Extension(1 Bit)
	private int CC = 0;		//CSRC Count(4 Bits) 
	private int M = 0;		//Marker(1 Bit)
	private int PT = 0;		//Payload Type(7 Bits)
	private int SN = 0;		//sequence Number(16 Bits)
	private long TS = 0;	//Timestamp(32 Bits)
	private long SSRC = 0;	//Sync. Source(32 Bits)
	
	//Nutzdatenteil -> payload (512 Bytes)
	private byte[] payLoad = null;
	private int payloadLength = 0;
	
	//leeres packet erzeugen
	public RtpPacket() {
		
	}
	

	public RtpPacket(byte[] packet, int length) {
		
		int lengthFromVToSN = 4; 
		byte[] bytesFromVToSN = new byte[lengthFromVToSN];
		
		System.arraycopy(packet, 0, bytesFromVToSN, 0, lengthFromVToSN);
		
		int intValueFromVToSN = this.convertBytesToInt(bytesFromVToSN);
				
		this.V = (intValueFromVToSN >>> 0x1E) & 0x03;
		this.P = (intValueFromVToSN >>> 0x1D) & 0x01;
		this.X = (intValueFromVToSN >>> 0x1C) & 0x01;
		this.CC = (intValueFromVToSN >>> 0x18) & 0x0F;
		this.M = (intValueFromVToSN>>> 0x17) & 0x01;
		this.PT = (intValueFromVToSN >>> 0x10) & 0x7F;
		this.SN = (intValueFromVToSN & 0xFFFF);      //0xFFFF -> 65536, 16 Bit
		int offset = lengthFromVToSN;
	
		int lengthOfTS = 4; 
		byte[] bytesFromTS = new byte[lengthOfTS];		
		System.arraycopy(packet, offset, bytesFromTS, 0, lengthOfTS);		
		byte[] bytesFromTSInLOng = new byte[8]; // Copy to long byte array
		System.arraycopy(bytesFromTS, 0, bytesFromTSInLOng, 4, 4);
		TS = this.convertBytesToLong(bytesFromTSInLOng);
		
		offset += lengthOfTS;
		
		int SSRC_length = 4; 
		byte[] SSRC_bytes = new byte[SSRC_length];
		System.arraycopy(packet, offset, SSRC_bytes, 0, SSRC_length);
		byte[] longSSRC_bytes = new byte[8]; 
		System.arraycopy(SSRC_bytes, 0, longSSRC_bytes, 4, 4);
		SSRC = convertBytesToLong(longSSRC_bytes);
		
		offset += SSRC_length;
		
				
		int payload_length = (length - offset); 
		payloadLength = payload_length;

		payLoad = new byte[payload_length];

		System.arraycopy(packet, offset, payLoad, 0, payload_length);
		
	}
	
	public void setV(int v) {
		
		if(v <= NUMBER_OF_MAX_VALUE_4_BIT && v >= 0)
			this.V = v;
		else
			throw new IllegalArgumentException("Version out of Range");
	}
	
	public void setP(int p) {
		
		if(p <= NUMBER_OF_MAX_VALUE_1_BIT && p >= 0)
			this.P = p;
		else
			throw new IllegalArgumentException("Padding out of Range");
	}
	
	public void setX(int x) {
		
		if(x <= NUMBER_OF_MAX_VALUE_1_BIT && x >= 0)
			this.X = x;
		else
			throw new IllegalArgumentException("Extension out of Range");
	
	}

	public void setCC(int cc) {
		
		if(cc <= NUMBER_OF_MAX_VALUE_16_BIT && cc >= 0)
			this.CC = cc;
		else
			throw new IllegalArgumentException("CSRC Count out of Range");
	}
	
	public void setM(int m) {
		
		if(m <= NUMBER_OF_MAX_VALUE_1_BIT && m >= 0)
			this.M = m;
		else
			throw new IllegalArgumentException("Marker out of Range");
	}
	
	public void setPT(int pt) {
		
		if(pt <= NUMBER_OF_MAX_VALUE_7_BIT && pt >= 0)
			this.PT = pt;
		else
			throw new IllegalArgumentException("Payload Type out of Range");
	}
	
	public void setSN(int sn) {
		
		if(sn <= NUMBER_OF_MAX_VALUE_16_BIT && sn >= 0)
			this.SN = sn;
		else
			throw new IllegalArgumentException("Sequence Number out of Range");
	}
	
	public void setTS(long ts) {
		
		if(ts <= NUMBER_OF_MAX_VALUE_32_BIT && ts >= 0)
			this.TS = ts;
		else
			throw new IllegalArgumentException("Timestamp out of Range");
	}
	
	public void setSSRC(long ssrc) {
		
		if(ssrc <= NUMBER_OF_MAX_VALUE_32_BIT && ssrc >= 0)
			this.SSRC = ssrc;
		else
			throw new IllegalArgumentException("Sync. Source out of Range");
	}	
	
	public void setPayload(byte[] payload, int payloadLength) {
		
		this.payLoad = payload;
		this.payloadLength = payloadLength;
	}

	public int getV() {
		
		return this.V;
	}
	
	public int getP() {
		
		return this.P;
	}
	
	public int getX() {
		
		return this.X;
	}

	public int getCC() {
		
		return this.CC;
	}
	
	public int getM() {
		
		return this.M;
	}
	
	public int getPT() {
		
		return this.PT;
	}
	
	public int getSN() {
		
		return this.SN;
	}
	
	public long getTS() {
		
		return this.TS;
	}
	
	public long getSSRC(int ssrc) {
		
		return this.SSRC;
	}

	public byte[] getPayload() {
		
		return this.payLoad;
	}
	
	public byte[] getRtpPacketinByte() {		
		
		int V_SN = 0;
		V_SN |= V;
		V_SN <<= 0x01; 
		V_SN |= P; 
		V_SN <<= 0x01; 
		V_SN |= X; 
		V_SN <<= 0x04; 
		V_SN |= CC; 
		V_SN <<= 0x01; 
		V_SN |= M; 
		V_SN <<= 0x07; 
		V_SN |= PT; 
		V_SN <<= 0x10; 
		V_SN |= SN; 
		byte[] V_SN_bytes = convertIntToBytes(V_SN);

	
		byte[] TS_bytes = convertIntToBytes((int) TS);

		
		byte[] SSRC_bytes = convertIntToBytes((int) SSRC);

		
		int length = V_SN_bytes.length + TS_bytes.length + SSRC_bytes.length
				+ payloadLength;

		byte[] data = new byte[length];

		int offset = 0;
		System.arraycopy(V_SN_bytes, 0, data, offset, V_SN_bytes.length);

		offset += V_SN_bytes.length;
		System.arraycopy(TS_bytes, 0, data, offset, TS_bytes.length);

		offset += TS_bytes.length;
		System.arraycopy(SSRC_bytes, 0, data, offset, SSRC_bytes.length);

		offset += SSRC_bytes.length;

		System.arraycopy(payLoad, 0, data, offset, payloadLength);

		return data;
	}
	
	public void buildRtpPacket() {
		
				
	}
	
	public void incrementSequenceNumber() {
		
		if(this.SN == 65536)
			this.SN = 0;
		else
			this.SN++;
	}
	
	public void printRtpPacket() {
		
		System.out.println("--RTP HEADER--");
		System.out.println("SIZE: "+this.payloadLength);
		
		
	}
	
	
	public int convertBytesToInt(byte[] bytes) {
		
		ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
		
		return byteBuffer.getInt();
	}
	
	public byte[] convertIntToBytes(int x) {
	
		ByteBuffer byteBuffer = ByteBuffer.allocate(4);	//int hat  4 bytes
		byteBuffer.putInt(x);
		
		return byteBuffer.array();
	}
	
	public long convertBytesToLong(byte[] bytes) {

		ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
		return byteBuffer.getLong();

	}
	
}
